package com.jmda.platform.repository.mybatis.scan;

import com.jmda.platform.commom.ResourcePathConfig;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;

/**
 * @version 2013年11月10日 下午1:43:45
 */
@Service
public class MybatisXmlMapperScan implements DisposableBean, InitializingBean, ApplicationContextAware {

	private ConfigurableApplicationContext context = null;
	private HashMap<String, String> fileMapping = new HashMap<String, String>();
	private Scanner scanner = null;
	private ScheduledExecutorService service = null;
	@javax.annotation.Resource
	ResourceLoader res;
	@javax.annotation.Resource
	ResourcePathConfig pathConfig;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.context = (ConfigurableApplicationContext) applicationContext;

	}

	public void reloadMybatisMapper() {
		try {
			new Scanner().reloadXML();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		reloadMybatisMapper();
	}

	@SuppressWarnings({ "rawtypes" })
	class Scanner {
		private String[] basePackages;
		private ResourcePatternResolver rpr = new PathMatchingResourcePatternResolver();
		public Scanner() {
			basePackages = new String[6];
			try {
				basePackages[0] = "URL:file://" + pathConfig.getFormPath() + "**_dao.xml";
				System.out.println(basePackages[0]);
				basePackages[1] = "URL:file://" + pathConfig.getWorklistPath() + "**_dao.xml";
				basePackages[2]="classpath*:/com/hbdiy/**/dao/**/*Mapper.xml";
				basePackages[3]="classpath*:/com/hbdiy/**/dao/**/*DAO.xml";
				basePackages[4]="classpath*:/com/hbdiy/**/dao/*DAO.xml";
				basePackages[5]="classpath*:/com/hbdiy/**/dao/*Mapper.xml";
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public Resource[] getResource(String basePackage) throws IOException {

			Resource[] resources = rpr.getResources(basePackage);
			return resources;
		}

		public void reloadXML() throws Exception {
			SqlSessionFactory factory = context.getBean(SqlSessionFactory.class);
			Configuration configuration = factory.getConfiguration();
			// 移除加载项
			removeConfig(configuration);
			// 重新扫描加载
			System.out.println("开始重新加载Mybatis所有的*Mapeper.xml 和*DAO.xml ");
			for (String basePackage : basePackages) {
				Resource[] resources = getResource(basePackage);
				if (resources != null) {
					for (int i = 0; i < resources.length; i++) {
						if (resources[i] == null) {
							continue;
						}
						try {
							XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resources[i].getInputStream(), configuration, resources[i].toString(), configuration.getSqlFragments());
							xmlMapperBuilder.parse();
						} catch (Exception e) {
							System.err.println("开始重新加载Mybatis>>>"+resources[i].getFile().getAbsolutePath()+"出错了，赶紧看看....");
//							System.exit(0);
							throw new NestedIOException("Failed to parse mapping resource: '" + resources[i] + "'", e);
						} finally {
							ErrorContext.instance().reset();
						}
					}
				}
			}
			System.out.println("重新加载加载Mybatis所有的*Mapeper.xml 和*DAO.xml 完成");
		}

		private void removeConfig(Configuration configuration) throws Exception {
			Class<?> classConfig = configuration.getClass();
			clearMap(classConfig, configuration, "mappedStatements");
			clearMap(classConfig, configuration, "caches");
			clearMap(classConfig, configuration, "resultMaps");
			clearMap(classConfig, configuration, "parameterMaps");
			clearMap(classConfig, configuration, "keyGenerators");
			clearMap(classConfig, configuration, "sqlFragments");
			clearSet(classConfig, configuration, "loadedResources");
		}

		private void clearMap(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {
			Field field = classConfig.getDeclaredField(fieldName);
			field.setAccessible(true);
			Map mapConfig = (Map) field.get(configuration);
			mapConfig.clear();
		}

		private void clearSet(Class<?> classConfig, Configuration configuration, String fieldName) throws Exception {
			Field field = classConfig.getDeclaredField(fieldName);
			field.setAccessible(true);
			Set setConfig = (Set) field.get(configuration);
			setConfig.clear();
		}

		public void scan() throws IOException {
			if (!fileMapping.isEmpty()) {
				return;
			}
			for (String basePackage : basePackages) {
				Resource[] resources = getResource(basePackage);
				if (resources != null) {
					for (int i = 0; i < resources.length; i++) {
						String multi_key = getValue(resources[i]);
						fileMapping.put(resources[i].getFilename(), multi_key);
					}
				}
			}
		}

		private String getValue(Resource resource) throws IOException {
			String contentLength = String.valueOf((resource.contentLength()));
			String lastModified = String.valueOf((resource.lastModified()));
			return new StringBuilder(contentLength).append(lastModified).toString();
		}

		public boolean isChanged() throws IOException {
			boolean isChanged = false;
			for (String basePackage : basePackages) {
				Resource[] resources = getResource(basePackage);
				if (resources != null) {
					for (int i = 0; i < resources.length; i++) {
						String name = resources[i].getFilename();
						String value = fileMapping.get(name);
						String multi_key = getValue(resources[i]);
						if (!multi_key.equals(value)) {
							isChanged = true;
							fileMapping.put(name, multi_key);
						}
					}
				}
			}
			return isChanged;
		}
	}

	@Override
	public void destroy() throws Exception {
		if (service != null) {
			service.shutdownNow();
		}
	}

}